/**
 * @file src/KTurnMotif.cpp
 *
 * @date 23-may-2013
 *
 * @author Youri Hoogstrate
 *
 * @section LICENSE
 * yh-kt-fold can predict RNA 2D structures including K-turns.
 * Copyright (C) 2012-2013 Youri Hoogstrate
 *
 * This file is part of yh-kt-fold.
 *
 * yh-kt-fold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * yh-kt-fold is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */



#include "KTurnMotif.h"



/**
 * @brief Matches if the KTurnMotif object matches a certain RNA sequence
 *
 * @param arg_motif
 * @param arg_seq
 *
 * @attention The must be certain functions around that already do this.
 *
 * @date 23-may-2012
 */
bool KTurnMotif::matchSequence(char arg_motif[], int arg_prime)
{
	int i, j;
	char char_motif, char_sequence;
	char *seq;
	
	switch(arg_prime)
	{
		case FIVE_PRIME:
			seq = this->motif5p;
			break;
		case THREE_PRIME:
			seq = this->motif3p;
			break;
		default:
			seq = (char *) "";
			break;
	}
	
	
	if(strlen(arg_motif) != strlen(seq))
	{
		return false;
	}
	
	j = (unsigned) strlen(seq);
	
	for(i = 0; i < j; i++)
	{
		char_motif = arg_motif[i];
		char_sequence = seq[i];
		
		switch(char_sequence)
		{
			case 'A':
				switch(char_motif)
				{
					case 'C':
					case 'U':
					case 'G':
					case 'Y':											// C || U (pyrimidines)
					case 'K':											// G || U (ketones)
					case 'S':											// G || C (strong)
					case 'B':											// C || G || U
						return false;
						break;
				}
				break;
			case 'C':
				switch(char_motif)
				{
					case 'A':
					case 'U':
					case 'G':
					case 'R':											// A || G (purine)
					case 'K':											// G || U (ketones)
					case 'W':											// A || U (weak)
					case 'D':											// A || G || U
						return false;
						break;
				}
				break;
			case 'G':
				switch(char_motif)
				{
					case 'C':
					case 'U':
					case 'A':
					case 'Y':											// C || U (pyrimidines)
					case 'M':											// A || C (amino groups)
					case 'W':											// A || U (weak)
					case 'H':											// A || C || U
						return false;
						break;
				}
				break;
			case 'U':
				switch(char_motif)
				{
					case 'C':
					case 'G':
					case 'A':
					case 'R':											// A || G (purine)
					case 'M':											// A || C (amino groups)
					case 'S':											// G || C (strong)
					case 'V':											// A || C || G
						return false;
						break;
				}
				break;
		}
	}
	
	return true;
}



/**
 * @brief Returns the Gibbs free energy
 *
 * @date 15-may-2013
 *
 * @return The Gibbs free energy for this KTurn.
 */
float KTurnMotif::getEnergy()
{
	return this->energy;
}



/**
 * @brief Validates if two characters statisfy a certain type of 'bond'.
 *
 * @param char1 Character of sequence 1
 * @param char2 Character of sequence 2
 * @param bond Type of bond
 *
 * @date 23-may-2013
 */
bool KTurnMotif::matchBond(char arg_char1, char arg_char2, char arg_bond)
{
	switch(arg_bond)
	{
		case KTURN__CANONICAL_PAIRING:									// '|' => Canonical pairing
			return isCanonicalPairing(arg_char1, arg_char2, true);
			break;
		case KTURN__CANONICAL_OR_WOBBLY_PAIRING:						// '+' => Canonical or Wobbly pairing
			return isCanonicalPairing(arg_char1, arg_char2, false);
			break;
		case KTURN__NO_PAIRING:
			return true;												// ':' => No pairing
			break;
	}
	return false;
}



/**
 * @brief
 *
 * @param arg_motif5p
 * @param arg_motif3p
 *
 * @date 25-may-2013
 */
bool KTurnMotif::match(char arg_motif5p[], char arg_motif3p[])
{
	int i;
	
	if(this->matchSequence(arg_motif5p, FIVE_PRIME) && this->matchSequence(arg_motif3p, THREE_PRIME))
	{
		for(i = 0; i < this->getSize(FIVE_PRIME); i++)
		{
			if(this->motif5pBonds[i] > -1)								// if i'th nucleotide in 5p is bound
			{
				if(!this->matchBond(arg_motif5p[i], arg_motif3p[motif5pBonds[i]], this->motif5pBondTypes[i]))
				{
					return false;
				}
			}
		}
		
		return true;
	}
	else
	{
		return false;
	}
}



/**
 * @brief Resets or (re-) initiates the class by setting variables to default values.
 *
 * @date 05-nov-2012
 *
 * @todo Figure out constants -1 and -2 and replace them for constants
 */
void KTurnMotif::reset()
{
	this->motif5p[0] = '\0';
	this->motif3p[0] = '\0';
	this->motif5pBondTypes[0] = '\0';
	this->motif5pBondTypes[0] = -2;
	
	this->energy = infinity;
}



/**
 * @brief
 *
 * @date 30-mar-2012
 *
 * @todo introduce a CONSTANT for UNSIGNED integer -> -10
 * @todo introduce a CONSTANT for UNBOUND integer -> -1/-3
 * @todo introduce a CONSTANT for BOUND integer -> 5, not specific for this function
 */
void KTurnMotif::setKTurn(char arg_name[], char arg_motif5p[], char arg_motif3p[], char arg_bonds[], float arg_energy)
{
	this->reset();
	
	int i, j, k, l;
	j = (unsigned) strlen(arg_name);
	
	/**
	 * @todo Use substr & strcpy?
	 */
	for(i = 0; (i < j) && (i < 31); i++)
	{
		this->name[i] = arg_name[i];
	}
	//if(i != 15)
	//{
	this->name[i] = '\0';
	//}
	
	this->energy = arg_energy;
	
	k = 0;
	l = 0;
	
	if(strlen(arg_motif5p) == strlen(arg_motif3p) && strlen(arg_motif5p) == strlen(arg_bonds))
	{
		j = (unsigned) strlen(arg_motif5p);
		
		// For char in string(s)
		for(i = 0; (i < j) && (i < 15); i++)
		{
			if(arg_motif5p[i] != ' ')
			{
				motif5p[k] = arg_motif5p[i];
				
				if(arg_bonds[i] != ' ' && arg_motif3p[i] != ' ')		// if there must be a bond & the bond refers to a valid 3p nucleotide..
				{
					// add bond
					this->motif5pBonds[k] = l;
					this->motif5pBondTypes[k] = arg_bonds[i];
				}
				else
				{
					this->motif5pBonds[k] = -1;
					this->motif5pBondTypes[k] = ' ';
				}
				
				k++;
			}
			if(arg_motif3p[i] != ' ')
			{
				this->motif3p[l] = arg_motif3p[i];
				l++;
			}
		}
		
		this->motif5p[k] = '\0';
		this->motif5pBonds[k] = -2;										// set as 'unsigned'; -1 means no binding -- Find constant for this!
		this->motif5pBondTypes[k] = '\0';
		this->motif3p[k] = '\0';
	}
}



/**
 * @brief get 5' or 3' size
 *
 * @param prime 5 or 3 prime motif. is wrong actually >> first and second should be better
 *
 * @date 22-may-2013
 */
int KTurnMotif::getSize(int arg_prime)
{
	switch(arg_prime)
	{
		case THREE_PRIME:
			return (unsigned) strlen(this->motif3p);
			break;
		case FIVE_PRIME:
			return (unsigned) strlen(this->motif5p);
			break;
		default:
			return -1;
			break;
	}
}



/**
 * @brief Give sequence of motif and put it into the second parameter.
 *
 * @param prime the direction of the RNA strand; 5prime or 3prime (5 || 3)
 * @param _str string where the motif should be inserted to
 *
 * @date 14-may-2013
 *//*
void KTurnMotif::getMotif(int prime, char *_str)
{
	switch(prime)
	{
		case THREE_PRIME:
			_str = motif3p;
			break;
		case FIVE_PRIME:
			_str = motif5p;
			break;
		default:
			_str[0] = '\0';												// Flush; unknown direction - Exit with error: assert? exit()?
			break;
	}
}*/
